﻿using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;

namespace TCP_UDP
{
    public class TcpServer
    {
        public event EventHandler Opened;
        public event EventHandler<IReceiveEventArgs> Received;
        public event EventHandler Closed;
        public event EventHandler<IErrorEventArgs> ErrorOccurred;

        Socket _socket;
        SocketAsyncEventArgs _event;
        List<TcpClient> _clients = new List<TcpClient>();
        ITcpConfiguration _tcpConfig;

        public TcpServer(ITcpConfiguration tcpConfig)
        {
            _tcpConfig = tcpConfig;
        }

        public void Init()
        {
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            Bind();
        }
        public void Send(byte[] buffer)
        {
            if (_clients.Count <= 0)
            {
                return;
            }
            TcpClient _client = _clients[0];
            _client.Send(buffer);
        }

        private void AcceptEvent_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessAccept();
        }

        #region 协议内部方法
        private void Bind()
        {
            if (_socket.LocalEndPoint == null)
            {
                _socket.Bind(_tcpConfig.LocalEndPoint);
                Listen();
                Accept();
            }
        }

        private void Listen()
        {
            _socket.Listen(_tcpConfig.Backlog);
        }

        private void Accept()
        {
            //_socket.Accept();
            _event.AcceptSocket = null;
            var isPending = _socket.AcceptAsync(_event);
            if (!isPending)
            {
                ProcessAccept();
            }
        }

        private void ProcessAccept()
        {
            //有客户端连接上了
            var client = new TcpClient(_tcpConfig);
            client.ErrorOccurred += ErrorOccurred;
            client.Closed += Closed;
            client.Received += Received;
            client.Closed += (s, e) => _clients.Remove(s as TcpClient);
            _clients.Add(client);
            client.Receive();

            Opened?.Invoke(this, new IOEventArgs { LocalEndPoint = _socket.LocalEndPoint, RemoteEndPoint = _event.AcceptSocket.RemoteEndPoint });
            Accept();
        }

        private void Close()
        {
            if (_socket != null)
            {
                if (_clients.Count > 0)
                {
                    var eventArgs = new IOEventArgs { LocalEndPoint = _socket.LocalEndPoint, RemoteEndPoint = null };

                    if (_socket.Connected)
                    {
                        _socket.Shutdown(SocketShutdown.Both);
                        eventArgs = new IOEventArgs { LocalEndPoint = _socket.LocalEndPoint, RemoteEndPoint = _socket.RemoteEndPoint };
                    }
                    _clients[0].MonitorClose();
                }
                _event.Completed -= AcceptEvent_Completed;
                _socket.Close();
                _socket.Dispose();
            }
        }
        #endregion



    }

}
